// #include "hash_table.h"


using namespace msl;

// ==============================
// Hash Map method's
// ==============================

template <typename K, typename T>
HashTableMap<K, T>::HashTableMap(unsigned int table_size)
    : collisions(0), table_size(table_size), el_count(0), els(table_size) {
    // empty 
}

template <typename K, typename T>
void HashTableMap<K, T>::insert(std::pair<K, T> el) {
    HashTableMap::insert(el.first, el.second);
}

template <typename K, typename T>
void HashTableMap<K, T>::insert(K el_key, T el_value) {
    int ind = hash(key_to_harg(el_key)) % table_size;
    
    for(auto el: els[ind]) {
        if(el.first == el_key) {
            el.second = el_value;
            return;
        }
    }

    if(!els[ind].empty()) collisions++;
    els[ind].push_back({el_key, el_value});
    el_count++;
}

template <typename K, typename T>
T HashTableMap<K, T>::find(K el_key) {
    int ind = hash(key_to_harg(el_key)) % table_size;
    for(auto el: els[ind]) {
        if(el.first == el_key) {
            return el.second;
        }
    }
    throw std::out_of_range("There is no element by this key");
}

template <typename K, typename T>
bool HashTableMap<K, T>::erase(K el_key) {
    int ind = hash(key_to_harg(el_key)) % table_size;
    for(auto it = els[ind].begin(); it != els[ind].end(); it++) {
        if((*it).first == el_key) {
            els[ind].erase(it);
            if(!els[ind].empty()) collisions--;
            el_count--;
            return true;
        }
    }
    return false;
}

template <typename K, typename T>
std::vector<T> HashTableMap<K, T>::get_all() {
    std::vector<T> res;
    for(auto l: els) {
        for(auto el: l) {
            res.push_back(el.second);
        }
    }
    return res;
}

template <typename K, typename T>
size_t HashTableMap<K, T>::size() {
    return this->el_count;
}

template <typename K, typename T>
unsigned int HashTableMap<K, T>::collision_count() {
    return this->collisions;
}

// ==============================
// Static secondary functions
// ==============================

template <typename K, typename T>
uint32_t HashTableMap<K, T>::key_to_harg(K key) {
    unsigned int byte_size = 0;
    uint32_t res = 0;
    for(auto el: key) {
        byte_size += sizeof(el);
        // if(byte_size > sizeof(uint32_t)) throw std::overflow_error("Hash value overflow");
        res = res * (1 << (sizeof(el) % sizeof(uint32_t) * 8)) + (uint32_t)(el);
    }
    return res;
}

template <typename K, typename T>
int HashTableMap<K, T>::hash(uint32_t harg) {
    const double A = 0.6180339887498949d; // (sqrt(5) - 1) / 2
    const int M = 1000;
    return (int)(M*((harg*A)-(int)(harg*A)));
}

